package com.iwuyc.tools.commons.util.string;

import com.iwuyc.tools.commons.exception.UnsupportedException;
import com.iwuyc.tools.commons.util.collection.ArrayUtil;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Optional;
import java.util.StringJoiner;
import java.util.UUID;

/**
 * @author Neil
 * @since 2017年10月15日
 */
public abstract class StringUtils {
    public static final String NIL_STRING = "";
    public static final String EMPTY_STRING = " ";
    public static final char BLANK_CHAR = ' ';
    public static final String SYSTEM_NEW_LINE = System.getProperty("line.separator");
    public static final String LINUX_NEW_LINE = "\n";
    public static final String WINDOWS_NEW_LINE = "\r\n";
    /**
     * 回车
     */
    public static final String CR = "\r";
    /**
     * 换行
     */
    public static final String LF = "\n";

    private StringUtils() {
    }

    /**
     * @param str 待判断的字符
     * @return 如果字符为null或者为""空字符，则返回true，否则返回false
     */
    public static boolean isEmpty(CharSequence str) {
        return null == str || str.length() == 0;
    }

    /**
     * isEmpty的取反
     * @param str 待判断的字符串
     * @see StringUtils#isEmpty(CharSequence)
     * @return 是否不是空字符串，具体请查看 {@link StringUtils#isEmpty(CharSequence)}，为该方法的取反
     */
    public static boolean isNotEmpty(CharSequence str) {
        return !isEmpty(str);
    }

    public static boolean equals(CharSequence one, CharSequence otherOne) {
        if (one == null || otherOne == null) {
            return one == otherOne;
        }
        return one.equals(otherOne);
    }

    /**
     * 反转字符串
     *
     * @param str 待反转的字符串
     * @return 反转后的字符串
     */
    public static String reverse(String str) {
        return new StringBuilder(str).reverse().toString();
    }

    /**
     * uuid
     *
     * @return uuid
     */
    public static String uuid() {
        return UUID.randomUUID().toString();
    }

    /**
     * 判断两个字符串是否不相等
     *
     * @param primaryStr 字符串1
     * @param slaveStr   字符串2
     * @return 如果相等，
     */
    public static boolean notEquals(CharSequence primaryStr, CharSequence slaveStr) {
        return !equals(primaryStr, slaveStr);
    }

    public static String turnFirstCharToLowerCase(String source) {
        if (isEmpty(source)) {
            return source;
        }
        char firstChar = source.charAt(0);
        if (firstChar < 'A' || firstChar > 'Z') {
            return source;
        }
        firstChar = (char) (firstChar - 'A' + 'a');
        return source.length() > 1 ? firstChar + source.substring(1) : String.valueOf(firstChar);
    }

    /**
     * 判断字符串是否为空字符串
     * <pre>
     * ex:
     * str = " "; : true
     * str = null; : true
     * str = " a "; : false
     * </pre>
     *
     * @param str 待检测的字符串
     * @return 如果为空字符串，则返回true，否则返回false
     */
    public static boolean isBlank(CharSequence str) {
        if (isEmpty(str)) {
            return true;
        }
        return str.chars().allMatch(item -> item == ' ');
    }

    /**
     * 判断字符串是否为空字符串，与 {@link #isBlank(CharSequence)} 方法结果相反
     *
     * @param str 待检测的字符串
     * @return 如果不为空字符串，则返回true，否则返回false
     */
    public static boolean isNotBlank(CharSequence str) {
        return !isBlank(str);
    }

    /**
     * 计算字符串的长度
     *
     * @param str 待计算的字符串
     * @return 返回字符串的长度，如果str为null则返回为0
     */
    public static int length(String str) {
        return str == null ? 0 : str.length();
    }

    /**
     * 判断字符串数组中是否存在空的字符串 {{@link #isEmpty(CharSequence)}}
     *
     * @param strs 待验证的字符串数组
     * @return 如果字符串数值中存在空字符串，则返回true，否则返回false
     */
    public static boolean isAnyEmpty(CharSequence... strs) {
        if (ArrayUtil.isEmpty(strs)) {
            return true;
        }
        for (CharSequence str : strs) {
            if (isEmpty(str)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 判断字符串数组中是否不存在空的字符串 !{@link #isAnyEmpty(CharSequence...)}
     *
     * @param strs 待验证的字符串数组
     * @return 如果字符串数组中存在空字符串，则返回false，否则返回true
     */
    public static boolean isNonEmpty(CharSequence... strs) {
        return !isAnyEmpty(strs);
    }

    /**
     * 去除字符串头尾的空白字符
     *
     * @param str 待去除的字符串
     * @return 已去除空白字符的字符串
     */
    public static String trim(String str) {
        if (str == null) {
            return null;
        } else {
            return str.trim();
        }
    }

    /**
     * 将所有非null的字符串以 delimiter 作为分隔符拼接起来，忽略为null或者为空的字符串，
     * 但会拼接所有字符都是空格的字符串
     *
     * @param delimiter 分隔符
     * @param elements  待拼接的字符串列表
     * @return 拼接后的字符串，如果elements为null，或者其中的字符串列表元素都为null，则返回 {@link Optional#empty()}
     */
    public static Optional<String> joinWithoutNull(String delimiter, String... elements) {
        if (ArrayUtil.isEmpty(elements)) {
            return Optional.empty();
        }
        final StringJoiner stringJoiner = new StringJoiner(delimiter);
        for (String element : elements) {
            if (StringUtils.isEmpty(element)) {
                continue;
            }
            stringJoiner.add(element);
        }
        return stringJoiner.length() == 0 ? Optional.empty() : Optional.of(stringJoiner.toString());
    }

    /**
     * 将字符串按 {@link java.nio.charset.StandardCharsets#UTF_8} 进行编码
     *
     * @param str 待编码的字符串
     * @return 编码后的字符串
     */
    public static String urlEncoding(String str) {
        return urlEncoding(str, StandardCharsets.UTF_8);
    }

    /**
     * 将字符串按指定字符集进行编码
     *
     * @param str     待编码的字符串
     * @param charset 字符集
     * @return 编码后的字符串
     * @throws UnsupportedException 如果提供了不支持的charset则会抛出该异常
     */
    public static String urlEncoding(String str, Charset charset) {
        try {
            return URLEncoder.encode(str, charset.name());
        } catch (UnsupportedEncodingException e) {
            throw new UnsupportedException("不支持的字符集", e);
        }
    }

    /**
     * 将字符串转换为byte数组，如果charset为null，则使用默认的编码
     *
     * @param data    待转换的字符串
     * @param charset 字符集,如果为null，则使用默认的编码
     * @return 转换后的byte数组
     * @see Charset#defaultCharset()
     */
    public static byte[] toBytes(CharSequence data, Charset charset) {
        if (isEmpty(data)) {
            return new byte[0];
        }
        final String dataString = data.toString();
        final Charset innerCharset = Optional.ofNullable(charset).orElse(Charset.defaultCharset());
        return dataString.getBytes(innerCharset);
    }

    /**
     * 将字符串转换为byte数组，使用默认的编码
     *
     * @param data 待转换的字符串
     * @return 转换后的byte数组
     */
    public static byte[] toBytes(CharSequence data) {
        return toBytes(data, null);
    }

    /**
     * 根据byte数组，创建字符串
     *
     * @param bytes   待转换的byte数组
     * @param charset 字符集，如果为null，则使用默认的编码
     * @return 转换后的字符串
     */
    public static String fromBytes(byte[] bytes, Charset charset) {
        final Charset innerCharset = Optional.ofNullable(charset).orElse(Charset.defaultCharset());
        return new String(bytes, innerCharset);
    }

    /**
     * 将字节数组转换成16进制字符串
     *
     * @param bytes 字节数组
     * @return 16进制字符串
     */
    public static String byteToHexString(byte[] bytes) {
        final StringBuilder resultBuild = new StringBuilder();
        for (byte b : bytes) {
            String tempStr = Integer.toHexString(b & 0xff);
            if (tempStr.length() == 1) {
                resultBuild.append('0').append(tempStr);
            } else {
                resultBuild.append(tempStr);
            }
        }
        return resultBuild.toString().toUpperCase();
    }
}
